import { $Http } from '@/config/service/http';
import SparkMD5 from 'spark-md5';

const codes = {
  pdm: 1,
  mes: 2,
  aps: 3,
  wms: 4,
  dms: 5,
  qms: 6,
  eam: 7,
  scada: 8,
  dt: 9
};
const systemName = import.meta.env.VITE_PUBLIC_PATH.split('/')
  .find((res) => res.includes('-'))
  .split('-')[0];

const getFileMD5 = (file: File): Promise<string> => {
  const CHUNK_SIZE = 10 * 1024 * 1024; // 10M
  const spark = new SparkMD5.ArrayBuffer();
  const fileReader = new FileReader();
  // 获取文件分片对象（注意它的兼容性，在不同浏览器的写法不同）
  // @ts-ignore
  const blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice;
  // 当前分片下标
  let currentChunk = 0;
  // 分片总数(向下取整)
  const chunks = Math.ceil(file.size / CHUNK_SIZE);
  // 加载下一个分片
  const loadNext = () => {
    const start = currentChunk * CHUNK_SIZE;
    const end = start + CHUNK_SIZE >= file.size ? file.size : start + CHUNK_SIZE;
    // 文件分片操作，读取下一分片(fileReader.readAsArrayBuffer操作会触发onload事件)
    fileReader.readAsArrayBuffer(blobSlice.call(file, start, end));
  };
  loadNext();

  return new Promise<string>((resolve, reject) => {
    fileReader.onload = (ev) => {
      spark.append(ev.target?.result);
      if (currentChunk < chunks) {
        currentChunk++;
        loadNext();
      } else {
        resolve(spark.end().toLocaleLowerCase());
      }
    };
    fileReader.onerror = (ev) => {
      console.error('文件对象 => ', file);
      console.error('文件读取错误 => ', ev.target?.error);
      reject('文件读取错误');
    };
  });
};

const getRequestUrlInfo = (category: number): { start: string; upload: string; end: string; cancel: string } => {
  switch (category) {
    case 1:
      return {
        start: 'dmsfile/personFileInfo/startMd5PartUpload',
        upload: 'dmsfile/personFileInfo/uploadMd5PartFile',
        end: 'dmsfile/personFileInfo/endMd5PartUpload',
        cancel: 'dmsfile/personFileInfo/closePartUpload'
      };
    case 2:
      return {
        start: 'dmsfile/projectFileInfo/startMd5PartUpload',
        upload: 'dmsfile/projectFileInfo/uploadMd5PartFile',
        end: 'dmsfile/projectFileInfo/endMd5PartUpload',
        cancel: 'dmsfile/projectFileInfo/closePartUpload'
      };
    case 3:
      return {
        start: 'dmsfile/deptFileInfo/startMd5PartUpload',
        upload: 'dmsfile/deptFileInfo/uploadMd5PartFile',
        end: 'dmsfile/deptFileInfo/endMd5PartUpload',
        cancel: 'dmsfile/deptFileInfo/closePartUpload'
      };
    default:
      return {
        start: 'dmsfile/resourceFileInfo/startMd5PartUpload',
        upload: 'dmsfile/resourceFileInfo/uploadMd5PartFile',
        end: 'dmsfile/resourceFileInfo/endMd5PartUpload',
        cancel: 'dmsfile/resourceFileInfo/closePartUpload'
      };
  }
};

const cacheRequest = {};

/**
 * 请求参数封装为 FormData 对象
 * @param params 参数
 * @returns {FormData}
 */
const packageFormData = (params) => {
  const data = new FormData();
  Object.keys(params).forEach(function (key) {
    data.append(key, params[key]);
  });
  return data;
};

const startMd5PartUpload = (url, fileParams, signal) => {
  return new Promise<any>((resolve, reject) => {
    return $Http
      .api(url, fileParams, 'post', '', '', { signal })
      .then((res) => {
        if (res.success) {
          resolve(res);
        } else {
          reject(res.message);
        }
      })
      .catch((err) => {
        reject(err);
      });
  });
};

/**
 * 切片上传
 * @param options 配置参数
 * @param options.category 默认资源文件，1=个人，2=项目，3=部门，4=资源文件
 * @param options.params 请求参数
 * @param options.file File文件对象
 * @param options.onUploadProgress 上传进度事件
 * @param options.filename File文件键名，默认 file
 * @param signal 中断信号
 * @returns { Promise }
 */
const uploadFile = (options, signal) => {
  const params = options.params || {}; // 没有参数默认为空JSON对象
  params[options.filename || 'file'] = options.file; // 没有规定file键名，默认为file
  const axiosOptions = {
    url: getRequestUrlInfo(options.category).upload,
    method: 'post',
    data: packageFormData(params)
  };
  // 判断是否需要上传进度反馈事件
  if (options.onUploadProgress) axiosOptions['onUploadProgress'] = options.onUploadProgress;
  return new Promise<any>((resolve, reject) => {
    return $Http
      .api(
        axiosOptions.url,
        axiosOptions.data,
        axiosOptions.method,
        '',
        '',
        Object.assign(
          { 'Content-Type': 'multipart/form-data' },
          options.onUploadProgress
            ? {
              onUploadProgress: options.onUploadProgress,
              signal
            }
            : { signal }
        ),
        60000
      )
      .then((res) => {
        if (res.success) {
          resolve(res);
        } else {
          reject(res.message);
        }
      })
      .catch((err) => {
        reject(err);
      });
  });
};

const endPartUpload = (url, fileParams, signal) => {
  return new Promise<any>((resolve, reject) => {
    return $Http
      .api(url, fileParams, 'post', '', '', { signal }, 60000)
      .then((res) => {
        if (res.success) {
          resolve(res);
        } else {
          reject(res.message);
        }
      })
      .catch((err) => {
        reject(err);
      });
  });
};

/**
 * 大文件上传
 * @param options 配置参数
 * @param options.category 默认0， 0: 资源文件，1: 个人文件，2: 部门文件，3: 项目文件
 * @param options.params 请求参数
 * @param options.file File文件对象
 * @param options.onUploadProgress 上传进度事件
 * @param options.filename File文件键名，默认 file
 * @returns { Promise }
 */
export async function uploadFileWithSlice(options: { category?: number; params?: any; file: File; onUploadProgress?: (event: { loaded: number; total: number }) => any; filename?: string }) {
  try {
    const fileId = await getFileMD5(options.file).catch((err) => Promise.reject(err));
    const fileSize = options.file.size;
    const sectionSize = 6 * 1024 * 1024; // 切片大小，超过该值将进行切片上传，字节(b)单位
    const sectionTotal = Math.ceil(options.file.size / sectionSize); // 切片次数
    const fileParams = Object.assign(
      {
        fileName: options.file.name,
        fileSize: fileSize,
        partNumber: sectionTotal,
        fileId: fileId,
        hashCode: fileId,
        subsystem: codes[systemName]
      },
      options.params ?? {}
    );

    cacheRequest[options.file.name] = [];
    const controller = new AbortController();
    cacheRequest[options.file.name].push(controller);
    const startMd5PartUploadRes = await startMd5PartUpload(getRequestUrlInfo(options.category).start, fileParams, controller.signal);

    if (startMd5PartUploadRes.data?.finish) {
      // 文件已存在
      const controller = new AbortController();
      cacheRequest[options.file.name].push(controller);
      return await endPartUpload(getRequestUrlInfo(options.category).end, fileParams, controller.signal);
    } else {
      // 记录已上传成功的分片下标和大小
      let finishSize = 0;
      const finishIndexes = startMd5PartUploadRes.data?.filePartUploadInfoList
        ?.filter((item) => {
          const start = item.index * sectionSize;
          const end = item.index === sectionTotal - 1 ? fileSize + 1 : start + sectionSize;
          return item.finish && Number(item.length) === end - start;
        })
        .map((item) => {
          finishSize += Number(item.length);
          return item.index;
        });
      const results = []; // 切片结果返回集
      const progressDict = {};
      for (let index = 0; index < sectionTotal; index++) {
        if (finishIndexes.indexOf(index) !== -1) continue; // 已上传的跳过
        const start = index * sectionSize;
        const end = index === sectionTotal - 1 ? fileSize + 1 : start + sectionSize;
        const packet = options.file.slice(start, end); //将文件进行切片

        const new_options = { ...options };
        new_options.file = <File>packet;
        new_options.params = {
          fileId: fileId,
          partIndex: index
        };
        if (options.onUploadProgress) {
          new_options.onUploadProgress = (progressEvent) => {
            progressDict[index] = progressEvent.loaded;
            options.onUploadProgress({
              loaded: (Object.values(progressDict) as number[]).reduce((a, b) => a + b) + finishSize,
              total: fileSize
            });
          };
        }
        const controller = new AbortController();
        cacheRequest[options.file.name].push(controller);
        results.push(await uploadFile(new_options, controller.signal));
      }
      await Promise.all(results);
      const controller = new AbortController();
      cacheRequest[options.file.name].push(controller);
      const res = await endPartUpload(getRequestUrlInfo(options.category).end, fileParams, controller.signal);
      delete cacheRequest[options.file.name];
      return res;
    }
  } catch (e) {
    return e;
  }
}

export const cancelUploadFileWithSliceRequest = (fileName?: string) => {
  if (fileName) {
    cacheRequest[fileName]?.forEach((item) => {
      item.abort();
    });
    delete cacheRequest[fileName];
  } else {
    Object.values(cacheRequest).forEach((arr: AbortController[]) => {
      arr.forEach((item) => {
        item.abort();
      });
    });
  }
};

/*
// 调用方法
uploadFileWithSlice({
	category: '文件分类', // 默认0， 0: 资源文件，1: 个人文件，2: 项目文件，3: 部门文件
	params: {},	// 请求携带的参数
	file: file,	// 上传的文件对象
	onUploadProgress: (event) => {
		console.log('已上传进度：', event.loaded, '总大小：', event.total)
	},
	filename: 'file',	// File文件键名
})
*/
